package com.test.process;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Names;
import com.test.anno.GSet;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes(value = "com.test.anno.GSet")
public class GSetAnnoProcessor extends AbstractProcessor {


    // 提供了待处理的抽象语法树
    private JavacTrees javacTrees;

    // 封装了创建AST节点的一些方法
    private TreeMaker treeMaker;

    // 提供了创建标识符的方法
    private Names names;


    /**
     * Initializes the processor with the processing environment by
     * setting the {@code processingEnv} field to the value of the
     * {@code processingEnv} argument.  An {@code
     * IllegalStateException} will be thrown if this method is called
     * more than once on the same object.
     *
     * @param processingEnv environment to access facilities the tool framework
     *                      provides to the processor
     * @throws IllegalStateException if this method is called more than once.
     */
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        ProcessingEnvironment environment = jbUnwrap(ProcessingEnvironment.class, processingEnv);
        this.javacTrees = JavacTrees.instance(environment);
        Context context = ((JavacProcessingEnvironment) environment).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);






    }

    private static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
        T unwrapped = null;
        try {
            final Class<?> apiWrappers = wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
            final Method unwrapMethod = apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
            unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
        }
        catch (Throwable ignored) {}
        return unwrapped != null? unwrapped : wrapper;
    }


    /**
     * @param annotations the annotation types requested to be processed
     * @param roundEnv    environment for information about the current and prior round
     * @return 修改语法树
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.err.println("process GSetAnnoProcessor start ... 修改语法树");
        // 获取注解标注的元素
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(GSet.class);
        if (!elements.isEmpty()) {
            for (Element element : elements) {
                // 根据注解标注的元素 获取语法树
                JCTree tree = this.javacTrees.getTree(element);
                if (tree != null) {
                    treeMaker.pos = tree.pos;
                    // 修改语法树 添加 get set 方法
                    tree.accept(new TreeTranslator() {
                        @Override
                        public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                            System.err.println("visitClassDef ... 修改语法树");
                            java.util.List<JCTree.JCVariableDecl> variables = new ArrayList<>();
                            Map<String, java.util.List<JCTree.JCMethodDecl>> methodMap = new HashMap<>();
                            // 在抽象树中找出所有变量
                            for (JCTree jcTree : jcClassDecl.defs) {
                                // JCTree.JCVariableDecl variable = (JCTree.JCVariableDecl) jcTree;
                                System.err.println("visitClassDef" + jcTree.getClass().getCanonicalName());
                                if (jcTree instanceof JCTree.JCVariableDecl) {
                                    JCTree.JCVariableDecl variable = (JCTree.JCVariableDecl) jcTree;
                                    variables.add(variable);
                                } else if (jcTree instanceof JCTree.JCMethodDecl) {
                                    JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) jcTree;
                                    if (!methodMap.containsKey(method.getName().toString())) {
                                        methodMap.put(method.getName().toString(), new ArrayList<>());
                                    }
                                    methodMap.get(method.getName().toString()).add(method);
                                }
                            }

                            if (!variables.isEmpty()) {
                                // 遍历变量列表 生成 get set 方法
                                for (JCTree.JCVariableDecl variable : variables) {

                                    // 检查 mods 字段中的 static 修饰符
                                    java.util.List<Integer> mods= variable.getModifiers().getFlags().stream().map(Enum::ordinal).collect(Collectors.toList());
                                    boolean isStatic = mods.contains(javax.lang.model.element.Modifier.STATIC.ordinal());
                                    boolean isPrivate = mods.contains(javax.lang.model.element.Modifier.PRIVATE.ordinal());
                                    boolean isProtected = mods.contains(javax.lang.model.element.Modifier.PROTECTED.ordinal());
                                    boolean isFinal = mods.contains(javax.lang.model.element.Modifier.FINAL.ordinal());
                                    // 非静态变量 非 final 变量 且 private 或者protected 修饰的变量
                                    // 则生成 get set 方法
                                    if (!isStatic && !isFinal && (isPrivate || isProtected)) {

                                        // 生成 get 方法
                                        makeGetMethod(jcClassDecl, variable, methodMap);
                                        // 生成 set 方法
                                        makeSetMethod(jcClassDecl, variable, methodMap);
                                    }
                                }
                            }
                        }
                    });
                }
            }
        }
        System.err.println("process GSetAnnoProcessor end ...修改语法树完成");
        return true;
    }

    private void makeSetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl variable, Map<String, java.util.List<JCTree.JCMethodDecl>> methodMap) {
        // 生成 set 方法
        String setName = "set" + variable.name.toString().substring(0, 1).toUpperCase() + variable.name.toString().substring(1);
        // 参数列表
        List<JCTree.JCVariableDecl> params = List.of(treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), variable.name, variable.vartype, null));
        // returnType
        JCTree.JCExpression returnType = treeMaker.Type(new Type.JCVoidType());

        // this.variableName = parameterName;
        JCTree.JCBlock body = treeMaker.Block(0, List.of(treeMaker.Exec(
                treeMaker.Assign(
                        treeMaker.Select(treeMaker.Ident(names._this), variable.name),   // this.variableName
                        treeMaker.Ident(variable.name)   // parameterName
                )
        )));
        JCTree.JCMethodDecl setMethod = treeMaker.MethodDef(treeMaker.Modifiers(Modifier.PUBLIC)
                , names.fromString(setName)
                , returnType
                , List.nil()  // 泛型形参列表
                , params, // 参数列表
                List.nil(),  // 异常列表
                body, // body 代码块/方法体
                null // 默认值 对于接口中的默认方法，此参数表示方法的默认实现。对于非默认方法，此参数通常为 null
        );
        if (methodMap.containsKey(setName)) {
            java.util.List<JCTree.JCMethodDecl> defs = methodMap.get(setName);
            for (JCTree.JCMethodDecl def : defs) {
                if (def.params.size() == 1 && isSameType(def.params.get(0).vartype, variable.vartype) && isSameType(def.getReturnType(), setMethod.getReturnType())) {
                    // 已存在同名方法，不再生成
                    System.err.println("已存在同名方法：" + setName);
                    return;
                }
            }
        }
        // 添加 set 方法到抽象语法树中
        jcClassDecl.defs = jcClassDecl.defs.append(setMethod);
    }

    private void makeGetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl variable, Map<String, java.util.List<JCTree.JCMethodDecl>> methodMap) {
        // 生成 get 方法
        String getName = "get" + variable.name.toString().substring(0, 1).toUpperCase() + variable.name.toString().substring(1);
        // returnType
        JCTree.JCExpression returnType = treeMaker.Type(variable.vartype.type);

        /**
         * @param mods 修饰符
         * @param name 方法名
         * @param returnType 返回值类型
         * @param TypeParameters 泛型形参列表
         * @param Parameters（参数列表）
         * @param Throws 异常列表 类型：List<JCTree.JCExpression>
         * @param body
         * @param defaultValue 默认值
         *
         */
        // return this.variableName;
        JCTree.JCBlock body = treeMaker.Block(0, List.of(treeMaker.Return(
                treeMaker.Select(treeMaker.Ident(names._this), variable.name)
        )));
        JCTree.JCMethodDecl getMethod = treeMaker.MethodDef(treeMaker.Modifiers(Modifier.PUBLIC)
                , names.fromString(getName)
                , returnType
                , List.nil()  // 泛型形参列表
                , List.nil(), // 参数列表
                List.nil(),  // 异常列表
                body, // body 代码块/方法体
                null // 默认值 对于接口中的默认方法，此参数表示方法的默认实现。对于非默认方法，此参数通常为 null
        );
        if (methodMap.containsKey(getName)) {
            java.util.List<JCTree.JCMethodDecl> defs = methodMap.get(getName);
            for (JCTree.JCMethodDecl def : defs) {
                if (def.params.isEmpty() && isSameType(def.getReturnType(), getMethod.getReturnType())) {
                    // 已存在同名方法，不再生成
                    System.err.println("已存在同名方法：" + getName);
                    return;
                }
            }
        }
        //
        jcClassDecl.defs = jcClassDecl.defs.append(getMethod);

    }

    public static boolean isSameType(JCTree type1, JCTree type2) {
        String name1 = getCanonicalName(type1);
        String name2 = getCanonicalName(type2);
        return name1.equals(name2) && !name1.equals("unknown");
    }

    public static String getCanonicalName(JCTree type) {
        // 泛型
        if (type instanceof JCTree.JCTypeApply) {
            JCTree.JCTypeApply typeApply = (JCTree.JCTypeApply) type;
            return getCanonicalName(typeApply.clazz) + "<" + typeApply.arguments.stream().map(GSetAnnoProcessor::getCanonicalName).collect(Collectors.joining(",")) + ">";
        } else if (type instanceof JCTree.JCIdent) {
            // 标识符类型 如 int, String, Object
            JCTree.JCIdent ident = (JCTree.JCIdent) type;
            return ident.sym.type.toString();
        } else if (type instanceof JCTree.JCFieldAccess) {
            // 字段访问类型
            JCTree.JCFieldAccess fieldAccess = (JCTree.JCFieldAccess) type;
            return getCanonicalName(fieldAccess.selected) + "." + fieldAccess.name.toString();
        } else if (type instanceof JCTree.JCArrayTypeTree) {
            // 数组类型
            JCTree.JCArrayTypeTree arrayType = (JCTree.JCArrayTypeTree) type;
            return getCanonicalName(arrayType.elemtype) + "[]";
        } else if (type instanceof JCTree.JCWildcard) {
            //一个泛型通配符
            JCTree.JCWildcard wildcard = (JCTree.JCWildcard) type;
            return "? extends " + getCanonicalName(wildcard.inner);
        } else if (type instanceof JCTree.JCTypeParameter) {
            // 参数类型
            JCTree.JCTypeParameter typeParameter = (JCTree.JCTypeParameter) type;
            return typeParameter.name.toString();
        } else if (type instanceof JCTree.JCTypeUnion) {
            // 联合类型
            JCTree.JCTypeUnion typeUnion = (JCTree.JCTypeUnion) type;
            return typeUnion.alternatives.stream().map(GSetAnnoProcessor::getCanonicalName).collect(Collectors.joining("|"));
        } else if (type instanceof JCTree.JCExpression) {
            // 表达式类型
            JCTree.JCExpression expression = (JCTree.JCExpression) type;
            return expression.toString();
        } else if (type instanceof JCTree.JCPrimitiveTypeTree) {
            // 表达式类型
            JCTree.JCPrimitiveTypeTree primitiveTypeTree = (JCTree.JCPrimitiveTypeTree) type;
            return primitiveTypeTree.toString();
        }
        return "Unknown";
    }
}
